#ifndef __REFAPP_STEERINGBEHAVIORS_H__
#define __REFAPP_STEERINGBEHAVIORS_H__

#include "OgreRefAppPrerequisites.h"
#include "OgreRefAppApplicationObject.h"

namespace OgreRefApp
{

    class SteeringBehavior
    {
    public:
        enum summing_method{weighted_average, prioritized, dithered};
    private:

        enum behavior_type
        {
            none               = 0x00000,
            seek               = 0x00002,
            flee               = 0x00004,
            arrive             = 0x00008,
            wander             = 0x00010,
            cohesion           = 0x00020,
            separation         = 0x00040,
            allignment         = 0x00080,
            obstacle_avoidance = 0x00100,
            wall_avoidance     = 0x00200,
            follow_path        = 0x00400,
            pursuit            = 0x00800,
            evade              = 0x01000,
            interpose          = 0x02000,
            hide               = 0x04000,
            flock              = 0x08000,
            offset_pursuit     = 0x10000,
        };

    private:


        //a pointer to the owner of this instance
        ApplicationObject*     m_pOwner;

        //the steering force created by the combined effect of all
        //the selected behaviors
        Ogre::Vector3    m_vSteeringForce;

        Real m_dInterposeDist;

        //these can be used to keep track of friends, pursuers, or prey
        ApplicationObject*     m_pTargetAgent1;
        ApplicationObject*     m_pTargetAgent2;

        //the current target
        Ogre::Vector3   m_vTarget;
        //binary flags to indicate whether or not a behavior should be active
        int          m_iFlags;

        //this function tests if a specific bit of m_iFlags is set
        bool      On(behavior_type bt);

        bool AccumulateForce(Ogre::Vector3 &RunningTot, Ogre::Vector3 ForceToAdd);

        //this behavior moves the agent towards a target position
        Ogre::Vector3 Seek(Ogre::Vector3 TargetPos);
        Ogre::Vector3 Arrive(Ogre::Vector3 t);
        Ogre::Vector3 Pursuit(ApplicationObject* agent);
        Ogre::Vector3 Evade(ApplicationObject* agent);
        Ogre::Vector3 Interpose(ApplicationObject* agentA, ApplicationObject* agentB);
        Ogre::Vector3 Separation(std::map<String, ApplicationObject*> agents);

        summing_method  m_SummingMethod;

    public:

        SteeringBehavior(ApplicationObject* agent);
        virtual ~SteeringBehavior();

        Ogre::Vector3 Calculate();

        //calculates and sums the steering forces from any active behaviors
        Ogre::Vector3 CalculateWeightedSum();
        Ogre::Vector3 CalculatePrioritized();
        Ogre::Vector3 CalculateDithered();

        void SetTargetAgent1(ApplicationObject* Agent)
        {
            m_pTargetAgent1 = Agent;
        }
        void SetTargetAgent2(ApplicationObject* Agent)
        {
            m_pTargetAgent2 = Agent;
        }
        void SeekOn(Ogre::Vector3 t)
        {
            m_iFlags |= seek;
            m_vTarget = t;
        }
        void SeekOn()
        {
            m_iFlags |= seek;
        }
        void SeekOff()
        {
            if (On(seek))   m_iFlags ^=seek;
        }
        bool isSeekOn()
        {
            return On(seek);
        }

        ApplicationObject*     GetTargetAgent1()
        {
            return m_pTargetAgent1;
        }
        ApplicationObject*     GetTargetAgent2()
        {
            return m_pTargetAgent2;
        }

        //the current target
        Ogre::Vector3   GetTarget()
        {
            return m_vTarget;
        }


        void SteeringBehavior::SetTarget(const Ogre::Vector3 t)
        {
            m_vTarget = t;
        }

        bool isArriveOn()
        {
            return On(arrive);
        }
        void ArriveOff()
        {
            if (On(arrive)) m_iFlags ^=arrive;
        }
        void ArriveOn(Ogre::Vector3 t)
        {
            m_iFlags |= arrive;
            m_vTarget = t;
        }
        void ArriveOn()
        {
            m_iFlags |= arrive;
        }


        bool isPursuitOn()
        {
            return On(pursuit);
        }
        void PursuitOff()
        {
            if (On(pursuit)) m_iFlags ^=pursuit;
        }
        void PursuitOn(ApplicationObject* v)
        {
            m_iFlags |= pursuit;
            m_pTargetAgent1 = v;
        }
        void PursuitOn()
        {
            m_iFlags |= pursuit;
        }


        bool isEvadeOn()
        {
            return On(evade);
        }
        void EvadeOn(ApplicationObject* v)
        {
            m_iFlags |= evade;
            m_pTargetAgent1 = v;
        }
        void EvadeOff()
        {
            if (On(evade)) m_iFlags ^=evade;
        }


        void InterposeOn(ApplicationObject* v1, ApplicationObject* v2)
        {
            m_iFlags |= interpose;
            m_pTargetAgent1 = v1;
            m_pTargetAgent2 = v2;
        }
        void InterposeOn(Real d)
        {
            m_iFlags |= interpose;
            m_dInterposeDist = d;
        }
        bool isInterposeOn()
        {
            return On(interpose);
        }
        void InterposeOff()
        {
            if (On(interpose)) m_iFlags ^=interpose;
        }

        void SeparationOn()
        {
            m_iFlags |= separation;
        }
        void SeparationOff()
        {
            if (On(separation)) m_iFlags ^=separation;
        }
        bool isSeparationOn()
        {
            return On(separation);
        }

        int getFlags()
        {
            return m_iFlags;
        }

    };


}

#endif


